/*****************************************************************************************/
/* ECSE 426 - Microprocessor - Fall 2009
/* Final Project (G08 and G14) - Bulls and Cows
/* GAME.C
/* @author: Danny Wu - 260238209 - B.Comp.Eng
/* @author: Han-Sheng Chin - 260209715 - B.Elec.Eng
/* @author: Benoit Gravel - 260230976 - B.Comp.Eng
/* @author: Bradley Thompson - 260224857 - B.Comp.Eng
/*
/* @version: 09.11.23.1748 
/****************************************************************************************/

#include <msp430x14x.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "game.h"
#include "lcd.h"
//#include "game.h"
#include "packet.h"
#include "constants.h"
#include "uart.h"
#include "main.h"
#include "serial.h"
#include "semphr.h" // semaphores

#define comSEND_DELAY			( ( portTickType ) 250/portTICK_RATE_MS )
#define comSTACK_SIZE				configMINIMAL_STACK_SIZE

extern char input;
extern int exit;
extern char gameSecretNum[4];  //Global variable for the secret 4 digit number.
extern int guessCount;
extern PACKET lastReceived;
extern char name[24];
extern state;
extern xSemaphoreHandle xKeySemaphore;
extern xSemaphoreHandle xStartSemaphore;

static portTASK_FUNCTION_PROTO( vGameFSM, pvParameters );

int gstate;   // Global variable for state of the game.
int gfunc;    // Global variable for the game input function.
int gexit;    // Global variable for determining whether to exit game.
int nameCount;
char guessNum[4];
extern char gameSecretNum[4];
static xComPortHandle xPort = NULL;



int secretCount, gCount;
int validCode, validGuess;
PACKET broadcast;
PACKET join;
PACKET current, guessPacket;
int i;
int acceptGuess = 0;
char replyGuess[2];
char addr;

static xTaskHandle gameFSM = NULL;

void xCreateGameTask( unsigned portBASE_TYPE uxPriority ){
  xTaskCreate( vGameFSM, ( signed char * ) "gFSM", comSTACK_SIZE+10, NULL, uxPriority, &gameFSM );
}

void vEndGameNow( void ){
  //vTaskSuspend(gameFSM);
  gstate = 0;
}

static portTASK_FUNCTION( vGameFSM, pvParameters ){
  //start the FSM of the game
  
  for(;;){
      switch(gstate){

      case 0:     // Print welcome menu and wait for user input.
        vEndPreGame();
        xEndAllHeart();
        printLCD("Welcome!");
        printLCD("Enter name:");     // Waits for player to enter name
        printLCD2("");
        nameCount = 0;
        gfunc = 0;
        gstate = 8;  // Wait
        break;

      case 1:     // Join or host option.
        printLCD("Name is: ");
        printLCD2(name);
        initPacket(spiReadReg(addr), 0xFF, name);
        printLCD("1 to join, 2 to host: ");
        gfunc = 1;
        gstate = 8;  // Wait
        break;
  
      case 2:     // Waiting for host.        
        printLCD("Waiting for host...");
        vGoPreGame( 0 );
        gfunc = 2;
        gstate = 22;
        receiveOnly();
        break;

      case 22:    // Loop while waiting for host.
        printLCD("Still waiting...");
        if(xSemaphoreTake( xStartSemaphore, portMAX_DELAY )){ // block for 2 seconds to save processr time
          vPlayGame();
          current = lastReceived;
          gstate = 4;
        }
        // If found host, change gstate to 5.
        break;

      case 3:     // Waiting for client.
        printLCD("Waiting for client...");
        vGoPreGame( 1 );
        gfunc = 2;
        gstate = 33;
        break;

      case 33:    // Loop while waiting for client.
        if(xSemaphoreTake( xStartSemaphore, portMAX_DELAY )){ // block for 2 seconds to save processr time
          vPlayGame();
          current = lastReceived;
          gstate = 5;
        }
        // If found client, change gstate to 4.
        break;

      case 4:     // Join confirmation.
        printLCD("Join");
        printLCD2(current.body);
        addr = current.src;
        printLCD("1: Yes, 2: No");
        gfunc = 3;
        gstate = 8;  // Wait
        break;

//      case 5:     // Connect to host. Start game.
//        createClientPacket(&join);
//        sendPacket(&join);      // Send reply to join host game.
//        if (lastReceived.type == 'Z')
//        {
//          setDestAddr(addr);
//          printLCD("Connected!");
//          printLCD("Enter your secret number: ");
//          secretCount = 0;
//          gfunc = 4;
//          gstate = 8; //Wait
//        }
//        else
//        {
//          printLCD("Unable to connect!");
//          gstate = 0;
//        }
//        break;

      case 5:
        printLCD("Connected!");
        printLCD("Enter your secret number: ");
        secretCount = 0;
        gfunc = 4;
        gstate = 8; //Wait
        break;

      case 6:   // Start guessing
        printLCD("Enter guess: ");
        acceptGuess = 1;
        gfunc = 5;
        gstate = 8;   // Wait
        break;

      case 9:   // Transmit guess
        acceptGuess = 0;
        createGuessPacket(&guessPacket, guessCount, guessNum);
        xSerialSendPkt( xPort, &guessPacket, comSEND_DELAY*2, 1 );
        printLCD("Guess sent!");
        if(xSemaphoreTake( xStartSemaphore, portMAX_DELAY )){
          guessCount++;
        }
        gstate = 6;
        break;

      case 7:     // Exit program.
        gexit = 1;
        break;

      case 8:     // Wait for input and in the meantime receive guesses.
        if(xSemaphoreTake( xKeySemaphore, portMAX_DELAY )){ // block for 2 seconds to save processr time
          //ccCommandStrobe(SIDLE);
        }
        break;
    }//end switch  
  }// end of while loop
}//end startFSM

//logic of the final state machine
void gameInput (char val)
{
  switch (gfunc) {
      case 0:                     // Username input
        switch(val)
        {
          case 0x1b:              // Escape key exits program
            gexit = 1;
            break;
          case 0x08:              // Backspace removes character from screen and 
            sendData('\b');       // Print \b on LCD
            sendData(0x20);
            putChar('\b');        // Print \b on terminal
            putChar(0x20);
            nameCount--;
            break;
          case 0x0d:              // Enter key confirms input and goes to next state.
            gstate = 1;           // Goes to join or host game menu.
            break;
          default:
            sendData(val);        // Print char on LCD
            putChar(val);         // Print char on terminal
            
            if (nameCount < 24)
            {
            name[nameCount] = val;
            nameCount++;
            }
            
            break;
        } // end nested switch of case func=0
        break;
      case 1:                                                  // Join or host game menu option.
        switch(val) 
        {
          input = 0;    // Set input to 0 first.
          case 0x31: // key '1': join game
            sendData(val);      // Print 1 on LCD
            putChar(val);
            input = 1;
            break;
          case 0x32: // key '2': host game
            sendData(val);      // Print 2 on LCD
            putChar(val);
            input = 2;
            break;          
          case 0x0d: // enter key: go to next state
            if (input == 1)
            {
              gstate = 2;
            }
            else if (input == 2)
            {
              gstate = 3;
            }
            else
            {
              printLCD("Invalid input! Try again!");
              gstate = 1;
            }
            break;
          case 0x1b: // escape key exits program
            gexit = 1;
            break;
          default:
            printLCD("Invalid input! Try again!");
            gstate = 1;
            break;
        } // end nested switch of case func=1
        break;
      case 2:                                                // Waiting for client or host state.
        switch(val)
        {
          case 0x1b: // Escape key
            printLCD("User aborted!");
            gstate = 1;
            break;
        } // end nested switch of case func=2
        break;
      case 3:                                               // Choose yes or no to join.
        switch(val) 
        {
          input = 0;    // Set input to 0 first.
          case 0x31: // key '1': join game
            sendData(val);      // Print 1 on LCD
            putChar(val);
            input = 1;
            break;
          case 0x32: // key '2': don't join game
            sendData(val);      // Print 2 on LCD
            putChar(val);
            input = 2;
            break;          
          case 0x0d: // enter key: go to next state
            if (input == 1)
            {
              gstate = 5;
            }
            else if (input == 2)
            {
              gstate = 1;
            }
            else
            {
              printLCD("Invalid input! Try again!");
              gstate = 1;
            }
            break;
          case 0x1b: // escape key exits program
            exit = 1;
            break;
          default:
            printLCD("Invalid input! Try again!");
            gstate = 1;
            break;
        } // end nested switch of case func=3
        break;
      case 4:               // Input secret number
        switch (val)
        {
          case 0x0d: // enter key: go to next state
            if (validCode == 1)
            {
              printLCD("Code saved!");
              gstate = 6;
            }
            else
            {
              printLCD("Invalid code! Try again!");
              gstate = 5;
            }          
          default:
            if (secretCount < 4)  // Check that there is less than 4 numbers entered for the code.
            {
              if (val < 0x40 && val > 0x29)   // Check that input is a number.
              {
                //putChar(val);
                sendData(val);
                gameSecretNum[secretCount] = val;
                secretCount++;
                validCode = 0;
              }
            }
            else if (secretCount = 4)
            {
              validCode = 1;
            }
            break;
          }
      case 5:               // Input guess number
        switch (val)
        {
          case 0x0d: // enter key: go to next state
            if (validGuess == 1)
            {
              printLCD("Guess saved!");
              gstate = 9;
            }
            else
            {
              printLCD("Invalid code! Try again!");
              gstate = 5;
            }          
          default:
            if (gCount < 4)  // Check that there is less than 4 numbers entered for the code.
            {
              if (val < 0x40 && val > 0x29)   // Check that input is a number.
              {
                putChar(val);
                sendData(val);
                guessNum[gCount] = val;
                gCount++;
                validGuess = 0;
              }
            }
            else if (gCount = 4)
            {
              validGuess = 1;
            }
            break;


        }
    } // end switch
}//end logicFSM


//returns a array of bulls and cows, first byte being the bull, the second, the cow
void getBullsAndCows(char guess[4], char bullCow[2]){
  int i;
  char bulls=0x30,cows=0x30;

  //look for bulls and cows 
  for(i=0; i < 4; i++) {
    if ( gameSecretNum[i] == guess[i] ) {
      bulls++;
    } else if ( strchr(gameSecretNum, guess[i]) != NULL ) {
      cows++;
    }//end if
  }//end for

  bullCow[0] = bulls;
  bullCow[1] = cows;
}//end getBullsAndCows

